/*
 
 File: AM_IO.mm
 
 Abstract: Helper functions for performing amplitude modulation.
 
 Portions of this file taken from Apple demo code.
 
 Author: Jasper Speicher, Tellart LLC 2008
 
 */

#import "AM_IO.h"
#import "AudioUnit/AudioUnit.h"
#import "CAXException.h"
#import "audio_helper.h"
#import "CADebugMacros.h"

@implementation AM_IO

@synthesize rioUnit;
@synthesize thruFormat;
@synthesize inputProc;
@synthesize peakAmp;
@synthesize initialized;
@synthesize enableSineGenerator;

void propListener(	void *                  inClientData,
                  AudioSessionPropertyID	inID,
                  UInt32                  inDataSize,
                  const void *            inData)
{
	AM_IO *THIS = (AM_IO*)inClientData;
	if (inID == kAudioSessionProperty_AudioRouteChange)
	{
		try {
			// if there was a route change, we need to dispose the current rio unit and create a new one
			XThrowIfError(AudioComponentInstanceDispose(THIS->rioUnit), "couldn't dispose remote i/o unit");		
            
			SetupRemoteIO(THIS->rioUnit, THIS->inputProc, THIS->thruFormat);
			
			UInt32 size = sizeof(THIS->hwSampleRate);
			XThrowIfError(AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &size, &THIS->hwSampleRate), "couldn't get new sample rate");
            
			XThrowIfError(AudioOutputUnitStart(THIS->rioUnit), "couldn't start unit");

			CFStringRef newRoute;
			size = sizeof(CFStringRef);
			XThrowIfError(AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &size, &newRoute), "couldn't get new audio route");
            
            printf("AM_IO: propListener - AU changed routing to:");
            printf("%s", newRoute);
            
		} catch (CAXException e) {
			char buf[256];
			fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
		}
		
	}
}

/*
 * dynamic perform-thru filter dispatcher
 *
 */
static OSStatus	PerformThru_dispatch(
							void						*inRefCon, 
							AudioUnitRenderActionFlags 	*ioActionFlags, 
							const AudioTimeStamp 		*inTimeStamp, 
							UInt32 						inBusNumber, 
							UInt32 						inNumberFrames, 
							AudioBufferList 			*ioData)
{
    AM_IO *THIS = (AM_IO *)inRefCon;
    //OSStatus err = AudioUnitRender(THIS->rioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData);
    OSStatus err = AudioUnitRender(
        THIS->rioUnit,      //AudioUnit inUnit,
        ioActionFlags,      //AudioUnitRenderActionFlags *ioActionFlags,
        inTimeStamp,        //const AudioTimeStamp *inTimeStamp,
        1,                  //UInt32 inOutputBusNumber,
        inNumberFrames,     //UInt32 inNumberFrames,
        ioData);            //AudioBufferList *ioData
        
    if (err) {
        printf("PerformThru_dispatch: error %d\n", (int)err);
        return err;
    /*} else {
        printf("PerformThru_dispatch: success\n");
    */
    }
    THIS->peakAmp = GetPeakAmp(ioData);

    if (THIS->enableSineGenerator) {
        //generate sine wave
        effect_SineGenerator(ioData);
    } else {
        //silence
        effect_Silence(ioData);
    }
    
    return err;
}

- (void)setSineToneGenerator:(BOOL)generateSineTone
{
    enableSineGenerator = generateSineTone;
}
- (void)initAudio:(BOOL)generateSineTone
{
	// Initialize our remote i/o unit
    [self setSineToneGenerator:generateSineTone];
    
    if (self.initialized) false; //don't initialize twice, it's bad

    inputProc.inputProc = PerformThru_dispatch;
	inputProc.inputProcRefCon = self;
    
	try {	
		// Initialize and configure the audio session
		XThrowIfError(AudioSessionInitialize(NULL, NULL, rioInterruptionListener, self), "couldn't initialize audio session");
		//moved: XThrowIfError(AudioSessionSetActive(true), "couldn't set audio session active\n");
        
		UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
		XThrowIfError(AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory), "couldn't set audio category");
		XThrowIfError(AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, self), "couldn't set property listener");

        //moved from above
		XThrowIfError(AudioSessionSetActive(true), "couldn't set audio session active\n");
        
		Float32 preferredBufferSize = 0.005;
		XThrowIfError(AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(preferredBufferSize), &preferredBufferSize), "couldn't set i/o buffer duration");
		
		UInt32 size = sizeof(hwSampleRate);
		XThrowIfError(AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &size, &hwSampleRate), "couldn't get hw sample rate");
		NSLog(@"hwSampleRate = %d", hwSampleRate);
        
		XThrowIfError(SetupRemoteIO(rioUnit, inputProc, thruFormat), "couldn't setup remote i/o unit");

		UInt32 maxFPS;
		size = sizeof(maxFPS);
		XThrowIfError(AudioUnitGetProperty(rioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFPS, &size), "couldn't get the remote I/O unit's max frames per slice");
		
		//fftBufferManager = new FFTBufferManager(maxFPS);
		//l_fftData = new int32_t[maxFPS/2];
		
		//oscilLine = (GLfloat*)malloc(drawBufferLen * 2 * sizeof(GLfloat));
        
		XThrowIfError(AudioOutputUnitStart(rioUnit), "couldn't start remote i/o unit");

		size = sizeof(thruFormat);
		XThrowIfError(AudioUnitGetProperty(rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &thruFormat, &size), "couldn't get the remote I/O unit's output client format");
        
        //flag that we've initialized the audio
        initialized = YES;
        
	}
	catch (CAXException &e) {
		char buf[256];
		fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
	}
	catch (...) {
		fprintf(stderr, "An unknown error occurred\n");
	}
}

@end
